/*
    Matali Physics Demo
    Copyright (c) 2011 KOMIRES Sp. z o. o.
 */
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Komires.MataliPhysics;

namespace MataliPhysicsDemo
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class SimpleCameraAnimation1
    {
        Demo demo;
        PhysicsScene scene;
        string instanceIndexName;

        string sphereName;
        string shotName;

        Shape sphere;

#if XBOX360
        GamePadState oldPadState;
#else
        MouseState oldMouseState;
        KeyboardState oldKeyboardState;
#endif

        Vector3 position;
        Vector3 direction;
        Matrix rotation;
        Matrix cameraRotation;

        Vector3 vectorZero;
        Matrix matrixIdentity;
        Quaternion quaternionIdentity;

        public SimpleCameraAnimation1(Demo demo, int instanceIndex)
        {
            this.demo = demo;
            instanceIndexName = " " + instanceIndex.ToString();

            sphereName = "Sphere";
            shotName = "Simple Camera Shot" + instanceIndexName + " ";

            vectorZero = Vector3.Zero;
            matrixIdentity = Matrix.Identity;
            quaternionIdentity = Quaternion.Identity;
        }

        public void Initialize(PhysicsScene scene)
        {
            this.scene = scene;
        }

        public void SetControllers()
        {
            sphere = scene.Factory.ShapeManager.Find("Sphere");

#if XBOX360
            oldPadState = GamePad.GetState(PlayerIndex.One);
#else
            oldMouseState = Mouse.GetState();
            oldKeyboardState = Keyboard.GetState();
#endif

            PhysicsObject objectBase = scene.Factory.PhysicsObjectManager.Find("Simple Camera" + instanceIndexName);
            if (objectBase != null)
            {
                objectBase.UserControllers.PostTransformMethods += new SimulateMethod(Move);
            }
        }

        public void RefreshControllers()
        {
#if XBOX360
            oldPadState = GamePad.GetState(PlayerIndex.One);
#else
            oldMouseState = Mouse.GetState();
            oldKeyboardState = Keyboard.GetState();
#endif
        }

        public void Move(SimulateMethodArgs args)
        {
            PhysicsScene scene = demo.Engine.Factory.PhysicsSceneManager.Get(args.OwnerSceneIndex);
            PhysicsObject objectBase = scene.Factory.PhysicsObjectManager.Get(args.OwnerIndex);

            if (!objectBase.Camera.Enabled) return;
            if (!objectBase.Camera.Active) return;

            float time = (float)args.Time;

            Vector3 deltaRotation = vectorZero;
            Vector3 deltaTranslation = vectorZero;
            float rotationSpeed = 8.0f;
            float translationSpeed = 8.0f;
            bool enableShot = false;

#if XBOX360
            GamePadState padState = GamePad.GetState(PlayerIndex.One);

            if (!demo.EnableMenu)
            {
                deltaRotation.Y += MathHelper.ToRadians(2.0f * rotationSpeed * padState.ThumbSticks.Right.X * time);
                deltaRotation.X -= MathHelper.ToRadians(2.0f * rotationSpeed * padState.ThumbSticks.Right.Y * time);

                if (padState.IsButtonDown(Buttons.RightTrigger) && oldPadState.IsButtonUp(Buttons.RightTrigger))
                    enableShot = true;

                deltaTranslation.X += padState.ThumbSticks.Left.X * translationSpeed * time;
                deltaTranslation.Z += padState.ThumbSticks.Left.Y * translationSpeed * time;
            }

            oldPadState = padState;
#else
            MouseState mouseState = Mouse.GetState();
            KeyboardState keyboardState = Keyboard.GetState();

            if (mouseState.RightButton == ButtonState.Pressed)
            {
                deltaRotation.Y += MathHelper.ToRadians(rotationSpeed * (mouseState.X - oldMouseState.X) * time);
                deltaRotation.X += MathHelper.ToRadians(rotationSpeed * (mouseState.Y - oldMouseState.Y) * time);
            }

            if ((mouseState.MiddleButton == ButtonState.Pressed) && (oldMouseState.MiddleButton == ButtonState.Released))
                enableShot = true;

            if ((keyboardState.IsKeyDown(Keys.RightControl) && oldKeyboardState.IsKeyUp(Keys.RightControl)) ||
               (keyboardState.IsKeyDown(Keys.LeftControl) && oldKeyboardState.IsKeyUp(Keys.LeftControl)))
                enableShot = true;

            if (keyboardState.IsKeyDown(Keys.W))
                deltaTranslation.Z += translationSpeed * time;

            if (keyboardState.IsKeyDown(Keys.S))
                deltaTranslation.Z -= translationSpeed * time;

            if (keyboardState.IsKeyDown(Keys.D))
                deltaTranslation.X += translationSpeed * time;

            if (keyboardState.IsKeyDown(Keys.A))
                deltaTranslation.X -= translationSpeed * time;

            oldMouseState = mouseState;
            oldKeyboardState = keyboardState;
#endif

            if (deltaRotation.LengthSquared() != 0.0f)
            {
                Vector3 euler = vectorZero;
                objectBase.Camera.GetEuler(ref euler);
                Vector3.Add(ref euler, ref deltaRotation, out euler);
                objectBase.Camera.SetEuler(ref euler);

                Matrix rotationX, rotationY;
                Matrix.CreateRotationX(-euler.X, out rotationX);
                Matrix.CreateRotationY(-euler.Y, out rotationY);
                Matrix.Multiply(ref rotationY, ref rotationX, out cameraRotation);

                objectBase.Camera.SetRotation(ref cameraRotation);

                objectBase.MainWorldTransform.SetTransposeRotation(ref cameraRotation);
                objectBase.RecalculateMainTransform();
            }

            if (deltaTranslation.LengthSquared() != 0.0f)
            {
                objectBase.MainWorldTransform.GetRotation(ref rotation);
                Vector3.Transform(ref deltaTranslation, ref rotation, out direction);

                objectBase.MainWorldTransform.GetPosition(ref position);
                Vector3.Add(ref position, ref direction, out position);
                objectBase.MainWorldTransform.SetPosition(ref position);

                objectBase.RecalculateMainTransform();
            }

            objectBase.Camera.Projection.CreatePerspectiveLH(1.0f, 10000.0f, 70.0f, demo.Window.ClientBounds.Width, demo.Window.ClientBounds.Height);

            objectBase.MainWorldTransform.GetPosition(ref position);
            objectBase.Camera.GetTransposeRotation(ref cameraRotation);

            objectBase.Camera.View.CreateLookAtLH(ref position, ref cameraRotation, 0.0f);
            objectBase.Camera.UpdateFrustum();

            if (enableShot)
            {
                PhysicsObject shot = scene.Factory.PhysicsObjectManager.Create(shotName + scene.SimulationFrameCount.ToString());

                shot.Shape = sphere;
                shot.UserTagStr = sphereName;

                Vector3 shotScale = vectorZero;
                shotScale.X = shotScale.Y = shotScale.Z = 0.5f;

                objectBase.MainWorldTransform.GetPosition(ref position);
                objectBase.Camera.GetTransposeRotation(ref cameraRotation);

                direction.X = cameraRotation.M31;
                direction.Y = cameraRotation.M32;
                direction.Z = cameraRotation.M33;

                Vector3.Multiply(ref direction, 300.0f, out direction);

                shot.InitLocalTransform.SetRotation(ref matrixIdentity);
                shot.InitLocalTransform.SetPosition(ref position);
                shot.InitLocalTransform.SetScale(ref shotScale);

                shot.InitLocalTransform.SetLinearVelocity(ref direction);
                shot.InitLocalTransform.SetAngularVelocity(ref vectorZero);
                shot.Integral.SetDensity(10.0f);
                shot.MaxSimulationFrameCount = 100;
                shot.EnableCollisions = true;
                shot.DisableCollision(objectBase, true);
                shot.MaxDisableCollisionFrameCount = 10;

                scene.UpdateFromInitLocalTransform(shot);
            }

            objectBase.Camera.UpdatePhysicsObjects(true, true);
            objectBase.Camera.SortDrawPhysicsObjects(PhysicsCameraSortOrderType.DrawPriorityShapePrimitiveType);
            objectBase.Camera.SortTransparentPhysicsObjects(PhysicsCameraSortOrderType.DrawPriorityShapePrimitiveType);
        }
    }
}
